import { createRouter, createWebHistory, NavigationFailure, Router, RouteRecordName, RouteRecordRaw } from "vue-router";

export class RouteConfig {
    public Path: string;
    public Name: string;
    public Components: Array<Array<string>>;

    public Children: Array<RouteConfig> | undefined;

    constructor(path: string, name: string, components: Map<string, string>, children: Array<RouteConfig> | undefined = undefined) {
        this.Path = path;
        this.Name = name;
        this.Components = Array.from(components);
        this.Children = children;
    }
}

export default class RouterComponent {
    private static instance: RouterComponent = new RouterComponent();
    public static get Default(): RouterComponent {
        return this.instance;
    }

    constructor() {
        window.addEventListener('unload', this.Dispose.bind(this));

        const index = {
            path: '/', name: '', components: {
                default: () => import("@components/Splash/SplashComponent.vue"),
                modal: () => import("@components/Modal/ModalComponent.vue"),
            }
        };
        this.Router.addRoute(index);

        for (const [key, module] of Object.entries(import.meta.glob('/src/components/**/*.vue'))) {
            this.modules[key] = module as () => Promise<{ [key: string]: any; }>;
        }

        const dynamicRoute = window.sessionStorage.getItem('DynamicRoute');
        if (dynamicRoute != null) {
            this.AddDynamicRoute(JSON.parse(dynamicRoute) as RouteConfig);
        }
    }

    Dispose() {
        window.removeEventListener('unload', this.Dispose);
    }

    public static SessionStorageKey: string = 'Routes';

    public Router: Router = createRouter(
        {
            history: createWebHistory(),
            routes: [],
        }
    );

    private modules: Record<string, () => Promise<{ [key: string]: any; }>> = {};

    public get CurrentRoutePath(): string {
        return this.Router.currentRoute.value.fullPath;
    }

    public AddDynamicRoute(config: RouteConfig, parent: string = ''): void {
        window.sessionStorage.setItem(`DynamicRoute`, JSON.stringify(config));
        this.Router.addRoute(this.GenerateRouteRecordRaw(parent, config, this.modules));
    }

    private GenerateRouteRecordRaw(parent: string, config: RouteConfig, modules: Record<string, () => Promise<{ [key: string]: any }>>): RouteRecordRaw {
        const path = `${parent}/${config.Path}`;

        const components: Record<string, () => Promise<{ [key: string]: any }>> = {};
        for (const [name, key] of config.Components) {
            components[name] = modules[key];
        }

        const children: Array<RouteRecordRaw> = [];
        if (config.Children != undefined) {
            for (const child of config.Children) {
                children.push(this.GenerateRouteRecordRaw(path, child, modules));
            }
        }

        return {
            name: config.Name,
            path,
            components,
            children,
        };
    }

    public Add(route: RouteRecordRaw, parent: RouteRecordName | undefined = undefined): void {
        if (parent == undefined) {
            this.Router.addRoute(route);
        } else {
            this.Router.addRoute(parent, route);
        }
    }

    public AddRange(routes: Array<RouteRecordRaw>): void {
        for (const route of routes) {
            this.Router.addRoute(route);
        }
    }

    /**
     * 从当前路由导航到下一界面
     * @param url 下一界面路由
     */
    public async NavigateToAsync(url: string): Promise<void | NavigationFailure | undefined> {
        await this.Router.push(url);
    }

    /**
     * 从当前路由导航到下一界面
     * @param name 子路由名称
     */
    public async NavigateToChildAsync(name: string): Promise<void | NavigationFailure | undefined> {
        const url = this.Router.currentRoute.value.fullPath;
        await this.Router.push(`${url}/${name}`);
    }

    /**
     * 返回至上一界面
     */
    public NavigateToPrevious(): void {
        this.Router.back();
    }
}